package vn.smartoffice.promail.controller;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import lombok.Getter;
import lombok.Setter;

import org.zkoss.bind.BindContext;
import org.zkoss.bind.BindUtils;
import org.zkoss.bind.annotation.BindingParam;
import org.zkoss.bind.annotation.Command;
import org.zkoss.bind.annotation.ContextParam;
import org.zkoss.bind.annotation.ContextType;
import org.zkoss.bind.annotation.GlobalCommand;
import org.zkoss.bind.annotation.Init;
import org.zkoss.bind.annotation.NotifyChange;
import org.zkoss.util.resource.Labels;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.OpenEvent;
import org.zkoss.zk.ui.select.annotation.VariableResolver;
import org.zkoss.zk.ui.select.annotation.WireVariable;
import org.zkoss.zkplus.spring.DelegatingVariableResolver;
import org.zkoss.zul.DefaultTreeModel;
import org.zkoss.zul.Messagebox;
import org.zkoss.zul.TreeModel;
import org.zkoss.zul.TreeNode;
import org.zkoss.zul.Window;

import vn.smartoffice.auth.UserService;
import vn.smartoffice.promail.dto.MailContactGroupNode;
import vn.smartoffice.promail.entity.MailContactGroup;
import vn.smartoffice.promail.services.MailContactGroupService;
import vn.smartoffice.promail.services.MailContactService;

@VariableResolver(DelegatingVariableResolver.class)
public class MailContactGroupCtr {

	@WireVariable private MailContactGroupService mailContactGroupService;
	@WireVariable private MailContactService mailContactService;
	@WireVariable private UserService userService;

	@Getter @Setter private MailContactGroupNode selectedNode;
	private MailContactGroupNode rootNode;

	public TreeModel<TreeNode<MailContactGroup>> getData() {
		return createTreeContactModel();
	}

	@Init
	public void init() {
		DefaultTreeModel<MailContactGroup> treeModel = createTreeContactModel();
		selectedNode = (MailContactGroupNode) treeModel.getChild(rootNode, 0);
	}

	@Command("createGroup")
	public void createGroup() {
		Map<String, Object> dataArgs = new HashMap<>();
		if (selectedNode != null) {
			dataArgs.put("node", selectedNode);

		} else {
			dataArgs.put("node", rootNode);
		}
		dataArgs.put("isCreateFolder", true);
		Window window = (Window) Executions.createComponents("dialog/contactGroup.zul", null, dataArgs);
		window.doModal();
	}

	@Command("renameGroup")
	public void renameGroup() {
		HashMap<String, Object> dataArgs = new HashMap<>();
		dataArgs.put("node", selectedNode);
		dataArgs.put("isCreateFolder", false);
		Window window = (Window) Executions.createComponents("dialog/contactGroup.zul", null, dataArgs);
		window.doModal();
	}

	@Command("deleteGroup")
	@NotifyChange({ "data", "selectedNode" })
	public void deleteGroup() {
		if (selectedNode.getParentId() != null) {
			Messagebox.show(Labels.getLabel("delete.messages"), Labels.getLabel("delete.title"), Messagebox.OK
					| Messagebox.CANCEL, Messagebox.QUESTION, new EventListener<Event>() {
				@Override
				public void onEvent(Event event) throws Exception {
					if (event.getName().equals("onOK")) {
						MailContactGroupNode parentNode = (MailContactGroupNode) selectedNode.getParent();
						mailContactGroupService.deleteById(selectedNode.getId());
						selectedNode = parentNode;
						BindUtils.postNotifyChange(null, null, MailContactGroupCtr.this, "selectedNode");
						BindUtils.postNotifyChange(null, null, MailContactGroupCtr.this, "data");
						Map<String, Object> args = new HashMap<String, Object>();
						args.put("base", parentNode);
						BindUtils.postGlobalCommand(null, null, "showMailContactList", args);
						getData();
					}
				}
			});
		} else {
			Messagebox.show(Labels.getLabel("macdinh.errorMessages"), Labels.getLabel("wraning.title"), Messagebox.OK,
					Messagebox.EXCLAMATION);
		}
	}

	@Command("setOpen")
	@NotifyChange("data")
	public void setOpen(@ContextParam(ContextType.BIND_CONTEXT) BindContext ctx,
			@BindingParam("item") MailContactGroupNode item) {
		OpenEvent event = (OpenEvent) ctx.getTriggerEvent();
		item.getData().setOpen(event.isOpen());
		if (!event.isOpen()) {
			closeNode(item);
		}
		mailContactGroupService.update(item.getData());

	}

	@Command("dropToNode")
	@NotifyChange("data")
	public void dropToNode(@BindingParam("item") MailContactGroupNode item,
			@BindingParam("base") MailContactGroupNode base) {
		if (item != null && base != null && (item.getParent() != base)) {
			addNode(base, item);
		}
	}

	@Command("loadSelectedGroup")
	public void loadSelectedGroup() {
		Map<String, Object> args = new HashMap<String, Object>();
		args.put("groupSelected", selectedNode.getData());
		BindUtils.postGlobalCommand(null, null, "loadCurrentGroupAdd", args);
	}

	@GlobalCommand
	@NotifyChange("data")
	public void refreshMailContactGroup() {
	}

	private void closeNode(MailContactGroupNode node) {
		if (node.getChildren().isEmpty()) {
			node.getData().setOpen(false);
			mailContactGroupService.update(node.getData());
		} else {
			for (MailContactGroupNode mailContactGroupNode : node.getChildrenNode()) {
				closeNode(mailContactGroupNode);
			}
			node.getData().setOpen(false);
			mailContactGroupService.update(node.getData());
		}
	}

	private void addNode(MailContactGroupNode parentNode, MailContactGroupNode childNode) {
		parentNode.add(childNode);
		MailContactGroup mailContactGroup = childNode.getData();
		mailContactGroup.setParentId(parentNode.getId());
		mailContactGroupService.update(mailContactGroup);
	}

	private MailContactGroupNode findById(List<MailContactGroupNode> mailContactGroupNodes, long id) {
		for (MailContactGroupNode mailContactGroupNode : mailContactGroupNodes) {
			if (mailContactGroupNode.getId() == id) {
				return mailContactGroupNode;
			}
		}
		return null;
	}

	private List<MailContactGroupNode> createListNode(List<MailContactGroup> mailContactGroups) {
		List<MailContactGroupNode> listNode = new ArrayList<MailContactGroupNode>();
		for (MailContactGroup mailContactGroup : mailContactGroups) {
			listNode.add(new MailContactGroupNode(mailContactGroup));
		}
		return listNode;
	}

	private MailContactGroupNode createRootNode(List<MailContactGroupNode> listNode) {
		MailContactGroupNode rootNode = new MailContactGroupNode(null);
		for (MailContactGroupNode mailContactGroupNode : listNode) {
			if (mailContactGroupNode.getParentId() == null) {
				rootNode.add(mailContactGroupNode);
			} else {
				findById(listNode, mailContactGroupNode.getParentId()).add(mailContactGroupNode);
			}
		}
		return rootNode;
	}

	private DefaultTreeModel<MailContactGroup> createTreeContactModel() {
		List<MailContactGroup> mailContactGroups = mailContactGroupService.findByUserId(userService.getCurrentUserId());
		rootNode = createRootNode(createListNode(mailContactGroups));
		DefaultTreeModel<MailContactGroup> treeModel = new DefaultTreeModel<>(rootNode);
		return treeModel;
	}

}
